package io.shuttle.mbe.api

import io.shuttle.mbe.api.annotation.ChromeMinVersion
import io.shuttle.mbe.api.annotation.ChromeOSOnly
import io.shuttle.mbe.api.annotation.PromiseStyleMinVersion
import io.shuttle.mbe.api.types.ArrayBuffer
import io.shuttle.mbe.api.types.Value1Function
import io.shuttle.mbe.api.types.Value2Function
import io.shuttle.mbe.api.types.VoidFunction
import kotlinx.serialization.SerialName
import io.shuttle.mbe.core.Promise

////////////////////
// Certificate Provider
////////////////////
/**
 * Use this API to expose certificates to the platform which can use these certificates for TLS authentications.
 *
 * Manifest: "certificateProvider"
 * @platform ChromeOS only
 * @since Chrome 46
 */
@ChromeMinVersion(46)
@ChromeOSOnly
interface CertificateProvider {
    // Should be called as a response to onSignatureRequested.
    //
    //The extension must eventually call this function for every onSignatureRequested event;
    // the API implementation will stop waiting for this call after some time and respond with a
    // timeout error when this function is called.
    @PromiseStyleMinVersion(96)
    @ChromeMinVersion(86)
    fun reportSignature(
        details: ReportSignatureDetails,
        callback: VoidFunction? = null
    ): Promise<Void>

    // Requests the PIN from the user. Only one ongoing request at a time is allowed.
    // The requests issued while another flow is ongoing are rejected.
    // It's the extension's responsibility to try again later if another flow is in progress.
    @PromiseStyleMinVersion(96)
    @ChromeMinVersion(57)
    fun requestPin(
        details: RequestPinDetails,
        callback: Value1Function<PinResponseDetails>? = null
    ): Promise<PinResponseDetails?>

    // Sets a list of certificates to use in the browser.
    //
    //The extension should call this function after initialization and on every change in the set
    // of currently available certificates.
    // The extension should also call this function in response to onCertificatesUpdateRequested
    // every time this event is received.
    @PromiseStyleMinVersion(96)
    @ChromeMinVersion(86)
    fun setCertificates(
        details: SetCertificatesDetails,
        callback: VoidFunction? = null,
    ): Promise<Void>

    // Stops the pin request started by the requestPin function.
    @PromiseStyleMinVersion(96)
    @ChromeMinVersion(57)
    fun stopPinRequest(
        details: StopPinRequestDetails,
        callback: VoidFunction? = null
    ): Promise<Void>

    // This event fires every time the browser requests the current list of certificates provided
    // by this extension. The extension must call reportCallback exactly
    // once with the current list of certificates.
    @ChromeMinVersion(47)
    @Deprecated(
        "Deprecated since Chrome 86",
        ReplaceWith("Use onCertificatesUpdateRequested instead.")
    )
    val onCertificatesRequested: Events.Event<Value1Function<Value2Function<List<CertificateInfo>, Value1Function<List<ArrayBuffer>>>>>

    // This event fires if the certificates set via setCertificates are insufficient or
    // the browser requests updated information.
    // The extension must call setCertificates with the updated list of certificates and the
    // received certificatesRequestId.
    @ChromeMinVersion(86)
    val onCertificatesUpdateRequested: Events.Event<Value1Function<CertificatesUpdateRequest>>

    // This event fires every time the browser needs to sign a message using a certificate provided by this extension via setCertificates.
    //
    //The extension must sign the input data from request using the appropriate algorithm and
    // private key and return it by calling reportSignature with the received signRequestId.
    @ChromeMinVersion(86)
    val onSignatureRequested: Events.Event<Value1Function<SignatureRequest>>

    // This event fires every time the browser needs to sign a message using a certificate
    // provided by this extension in reply to an onCertificatesRequested event.
    // The extension must sign the data in request using the appropriate algorithm and private
    // key and return it by calling reportCallback. reportCallback must be called exactly once.
    @Deprecated("Deprecated since Chrome 86", ReplaceWith("Use onSignatureRequested instead."))
    val onSignDigestRequested: Events.Event<Value2Function<SignRequest, Value1Function<ArrayBuffer>>>

    // Types of supported cryptographic signature algorithms.
    @ChromeMinVersion(86)
    enum class Algorithm {
        // Specifies the RSASSA PKCS#1 v1.5 signature algorithm with the MD5-SHA-1 hashing.
        // The extension must not prepend a DigestInfo prefix but only add PKCS#1 padding.
        // This algorithm is deprecated and will never be requested by Chrome as of version 109.
        RSASSA_PKCS1_v1_5_MD5_SHA1,

        // Specifies the RSASSA PKCS#1 v1.5 signature algorithm with the SHA-1 hash function.
        RSASSA_PKCS1_v1_5_SHA1,

        // Specifies the RSASSA PKCS#1 v1.5 signature algorithm with the SHA-256 hashing function.
        RSASSA_PKCS1_v1_5_SHA384,

        // Specifies the RSASSA PKCS#1 v1.5 signature algorithm with the SHA-384 hashing function.
        RSASSA_PKCS1_v1_5_SHA512,

        // Specifies the RSASSA PSS signature algorithm with the SHA-256 hashing function,
        // MGF1 mask generation function and the salt of the same size as the hash.
        RSASSA_PSS_SHA256,

        // Specifies the RSASSA PSS signature algorithm with the SHA-384 hashing function,
        // MGF1 mask generation function and the salt of the same size as the hash.
        RSASSA_PSS_SHA384,

        // Specifies the RSASSA PSS signature algorithm with the SHA-512 hashing function,
        // MGF1 mask generation function and the salt of the same size as the hash.
        RSASSA_PSS_SHA512,
    }

    data class CertificateInfo(
        // Must be the DER encoding of a X.509 certificate.
        // Currently, only certificates of RSA keys are supported.
        var certificate: ArrayBuffer,
        // Must be set to all hashes supported for this certificate.
        // This extension will only be asked for signatures of digests calculated with one of these hash algorithms.
        // This should be in order of decreasing hash preference.
        var supportedHashed: List<Hash>
    )

    @ChromeMinVersion(86)
    data class CertificatesUpdateRequest(
        // Request identifier to be passed to setCertificates.
        var certificatesRequestId: Number,
    )

    data class ClientCertificateInfo(
        // The array must contain the DER encoding of the X.509 client certificate as its first element.
        // This must include exactly one certificate.
        var certificateChain: List<ArrayBuffer>,
        // All algorithms supported for this certificate.
        // The extension will only be asked for signatures using one of these algorithms.
        var supportedAlgorithms: List<Algorithm>,
    )

    // Types of errors that the extension can report.
    @ChromeMinVersion(86)
    enum class Error {
        @SerialName("GENERAL_ERROR")
        GeneralError
    }

    // The types of errors that can be presented to the user through the requestPin function.
    @ChromeMinVersion(57)
    enum class PinRequestErrorType {
        @SerialName("INVALID_PIN")
        InvalidPin,

        @SerialName("INVALID_PUK")
        InvalidPuk,

        @SerialName("MAX_ATTEMPTS_EXCEEDED")
        MaxAttemptsExceeded,

        @SerialName("UNKNOWN_ERROR")
        UnknownError
    }

    // The type of code being requested by the extension with requestPin function.
    @ChromeMinVersion(57)
    enum class PinRequestType {
        PIN,
        PUK
    }

    @ChromeMinVersion(57)
    data class PinResponseDetails(
        // The code provided by the user.
        // Empty if user closed the dialog or some other error occurred.
        var userInput: String? = null,
    )

    @ChromeMinVersion(86)
    data class ReportSignatureDetails(
        // Error that occurred while generating the signature, if any.
        var error: Error? = null,
        // Request identifier that was received via the onSignatureRequested event.
        var signRequestId: Number,
        // The signature, if successfully generated.
        var signature: ArrayBuffer? = null,
    )

    @ChromeMinVersion(57)
    data class RequestPinDetails(
        // The number of attempts left. This is provided so that any UI can present this information to the user.
        // Chrome is not expected to enforce this,
        // instead stopPinRequest should be called by the extension with errorType = MAX_ATTEMPTS_EXCEEDED when the number of pin requests is exceeded.
        var attemptsLeft: Number? = null,
        // The error template displayed to the user.
        // This should be set if the previous request failed,
        // to notify the user of the failure reason.
        var errorType: PinRequestErrorType? = null,
        // The type of code requested. Default is PIN.
        var requestType: PinRequestType? = PinRequestType.PIN,
        // The ID given by Chrome in SignRequest.
        var signRequestId: Number,
    )

    @ChromeMinVersion(86)
    data class SetCertificatesDetails(
        // When called in response to onCertificatesUpdateRequested,
        // should contain the received certificatesRequestId value.
        // Otherwise, should be unset.
        var certificatesRequestId: Number? = null,
        // Array of currently available client certificates.
        var clientCertificates: List<ClientCertificateInfo>,
        // Error that occurred while extracting the certificates, if any.
        // This error will be surfaced to the user when appropriate.
        var error: Error? = null,
    )

    @ChromeMinVersion(86)
    data class SignatureRequest(
        // Signature algorithm to be used.
        var algorithm: Algorithm,
        // The DER encoding of a X.509 certificate.
        // The extension must sign input using the associated private key.
        var certificate: ArrayBuffer,
        // Data to be signed. Note that the data is not hashed.
        var input: ArrayBuffer,
        // Request identifier to be passed to reportSignature.
        var signRequestId: Number,
    )

    data class SignRequest(
        // The DER encoding of a X.509 certificate.
        // The extension must sign digest using the associated private key.
        var certificate: ArrayBuffer,
        // The digest that must be signed.
        var digest: ArrayBuffer,
        // Refers to the hash algorithm that was used to create digest.
        var hash: Hash,
        // The unique ID to be used by the extension should it need to call a method that requires it, e.g. requestPin.
        @ChromeMinVersion(57)
        var signRequestId: Number,
    )

    @ChromeMinVersion(57)
    data class StopPinRequestDetails(
        // The error template. If present it is displayed to user.
        // Intended to contain the reason for stopping the flow if it was caused by an error,
        // e.g. MAX_ATTEMPTS_EXCEEDED.
        var errorType: PinRequestErrorType? = null,
        // The ID given by Chrome in SignRequest.
        var signRequestId: Number,
    )


    @Deprecated("Replaced by Algorithm.")
    enum class Hash {
        MD5_SHA1,
        SHA1,
        SHA256,
        SHA384,
        SHA512,
    }
}